// 接口：  用来描述数据的形状  接口中没有具体的实现（抽象的）
// 接口：  可以描述函数 对象  类

// interface和 type的区别
// type:  可以使用联合类型   不可重名   无法扩展   可以循环类型in
// interface: 不支持联合类型  同名接口自动合并  可以继承  不可用in

interface ISum {
  // 混合接口  描述函数和函数上的属性
  (s: string, b: string): string;
  a: number;
}

type ISum1 = ((a: string, b: string) => string) | number;

const sum: ISum1 = (x: string, y: string): string => x + y;

// let sum1: ISum = (x: string, y: string): string => x + y; // 确少a属性
const sum2: ISum = (x: string, y: string): string => x + y;
sum2.a = 100; // let 和const 的区别？

const fn = (x: string, y: string): string => x + y;
fn.a = 100;
let sum1: ISum = fn;

/** 额外属性的声明方式
 * 1、 利用ts的兼容性
 * 2、使用接口的自动合并
 * 3、基于原有类型extends进行扩展
 * 4、断言为已经存在的属性
 * 5、任意接口 通过[key:string]:any
 *
 */

//-------------------------描述object----------------
interface IFruit {
  color: () => string;
  size: number;
}
let fruit: IFruit = {
  color: () => "abc",
  size: 13,
  // xxx:123 // error
};
// 利用接口的兼容性----添加未知属性  xxx不可取值
let o = {
  color: () => "abc",
  size: 13,
  xxx: 123,
};
let fruit1: IFruit = o;

// 利用断言----添加未知属性  xxx不可取值
let fruit2: IFruit = {
  color: () => "abc",
  size: 13,
  xxx: 123,
} as IFruit;

// 利用接口继承 （可多个）----添加未知属性 xxx可取值
interface IB {
  b: number;
}
interface INewFruit extends IFruit, IB {
  xxx: number;
  a?: number; // ?表示类型可有可无
}
let fruit3: INewFruit = {
  color: () => "abc",
  size: 13,
  xxx: 123,
  a: 123,
  b: 1,
};

//------------接口的使用方式

interface IV {
  readonly color: string; // 只读
  taste: string;
  size?: number; // ? 可以可无
}

let veg: IV = {
  color: "red",
  taste: "sweet",
};
veg.size;
// -------veg.color = "11"; //error  color 为只读属性
// ------- as const   标识为只读属性  只允许放类型 不允许放变量
const x = { text: 123 } as const;
// x.text = 333    // error   标识为只读属性

//  同名接口自动合并
interface IFruit1 {
  name: string;
  color: string;
}
interface IFruit1 {
  age: string;
}

let fruit4: IFruit1 = {
  name: "ap",
  color: "red",
  age: "18",
};

//  定义接口 key的类型

interface IFruit3 extends IFruit1 {
  [key: string]: any; // key只允许为string| number |symbol  都支持Symbol
}

let fruit5: IFruit3 = {
  name: "ap",
  color: "red",
  age: "18",
  other: "额外", // key为number时报错
  1: 100,
  [Symbol()]: 299,
};

interface IArr {
  [key: number]: any;
}
let arr: IArr = [1, 2, 3, 4];
let obj: IArr = [{ 0: 1 }, { 1: 1 }];

//-------------------------描述class---

/* abstract 标识为一个抽象类 只应用于ts 无法被new
  抽象类中  可以定义非抽象类的方法
  抽象方法： 抽象类中的方法  必须在子类中实现
*/

/* 
  extends 继承  只允许继承单个
  implements 允许继承多个 
*/

abstract class Animal {
  abstract name: string;
  abstract eat(): void;
  speak() {
    console.log("speak");
  }
}

class Cat extends Animal {
  name: string = "cat";
  eat(): void {}
}
let cat = new Cat();
cat.speak();
cat.eat();

// -----接口类型的方法声明写法的不同 意义不同
interface IEat {
  eat: () => string; // 描述是实例上的方法
}

interface IDrink {
  drink(): void; //描述的是原型上的方法
}

class Tom implements IEat, IDrink {
  //
  eat!: () => string; // 实例方法
  // constructor() {
  //   this.eat = () => {};
  // }

  drink() {
    console.log("原型上的方法");
  }
}

let tom = new Tom();
tom.drink;
tom.eat;

//接口描述 构造函数

class Mouse {
  constructor(public name: string, public age: number) {}
}
class Dog {
  constructor(public name: string, public age: number) {}
}

// 类只能描述实例  补鞥呢描述类本身
// 描述类本身  需要采用typeof 进行描述

// type IClazz = new (name: string, age: number) => any;
interface IClazz {
  new (name: string, age: number): any;
}
function getInstance(clazz: IClazz, name: string, age: number) {
  // 描述构造函数 需要返回一个any实例
  return new clazz(name, age);
}

getInstance(Mouse, "jy", 13);
